//
//  UIView+ADUtil.m
//  ADKit
//
//  Created by duzexu on 2017/12/1.
//  Copyright © 2017年 duzexu. All rights reserved.
//

#import "UIView+ADUtil.h"
#import <objc/runtime.h>

static char kADActionHandlerTapBlockKey;
static char kADActionHandlerTapGestureBlockKey;
static char kADActionHandlerTapGestureKey;
static char kADActionHandlerTapFrameKey;
static char kADActionHandlerLongPressBlockKey;
static char kADActionHandlerLongPressGestureKey;

static char kADRedDotKey;

@implementation UIView (ADUtil)

+ (instancetype)ad_instanceWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundleOrNil owner:(id)owner {
    //default values
    NSString *nibName = nibNameOrNil ?: NSStringFromClass(self);
    NSBundle *bundle = bundleOrNil ?: [NSBundle mainBundle];
    
    //cache nib to prevent unnecessary filesystem access
    static NSCache *nibCache = nil;
    if (nibCache == nil)
    {
        nibCache = [[NSCache alloc] init];
    }
    NSString *pathKey = [NSString stringWithFormat:@"%@.%@", bundle.bundleIdentifier, nibName];
    UINib *nib = [nibCache objectForKey:pathKey];
    if (nib == nil)
    {
        NSString *nibPath = [bundle pathForResource:nibName ofType:@"nib"];
        if (nibPath) nib = [UINib nibWithNibName:nibName bundle:bundle];
        [nibCache setObject:nib ?: [NSNull null] forKey:pathKey];
    }
    else if ([nib isKindOfClass:[NSNull class]])
    {
        nib = nil;
    }
    
    if (nib)
    {
        //attempt to load from nib
        NSArray *contents = [nib instantiateWithOwner:owner options:nil];
        UIView *view = [contents count]? [contents objectAtIndex:0]: nil;
        NSAssert ([view isKindOfClass:self], @"First object in nib '%@' was '%@'. Expected '%@'", nibName, view, self);
        return view;
    }
    
    //return empty view
    return [[[self class] alloc] init];
}

+ (instancetype)ad_instanceFromNib {
    return [self ad_instanceWithNibName:NSStringFromClass(self) bundle:nil owner:self];
}

- (void)ad_loadContentsWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundleOrNil
{
    NSString *nibName = nibNameOrNil ?: NSStringFromClass([self class]);
    UIView *view = [UIView ad_instanceWithNibName:nibName bundle:bundleOrNil owner:self];
    if (view)
    {
        if (CGSizeEqualToSize(self.frame.size, CGSizeZero))
        {
            //if we have zero size, set size from content
            self.ad_size = view.ad_size;
        }
        else
        {
            //otherwise set content size to match our size
            view.frame = self.ad_contentBounds;
        }
        [self addSubview:view];
    }
}

//view searching

- (UIView *)ad_viewMatchingPredicate:(NSPredicate *)predicate
{
    if ([predicate evaluateWithObject:self])
    {
        return self;
    }
    for (UIView *view in self.subviews)
    {
        UIView *match = [view ad_viewMatchingPredicate:predicate];
        if (match) return match;
    }
    return nil;
}

- (UIView *)ad_viewWithTag:(NSInteger)tag ofClass:(Class)viewClass
{
    return [self ad_viewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused NSDictionary *bindings) {
        return [evaluatedObject tag] == tag && [evaluatedObject isKindOfClass:viewClass];
    }]];
}

- (UIView *)ad_viewOfClass:(Class)viewClass
{
    return [self ad_viewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused NSDictionary *bindings) {
        return [evaluatedObject isKindOfClass:viewClass];
    }]];
}

- (NSArray *)ad_viewsMatchingPredicate:(NSPredicate *)predicate
{
    NSMutableArray *matches = [NSMutableArray array];
    if ([predicate evaluateWithObject:self])
    {
        [matches addObject:self];
    }
    for (UIView *view in self.subviews)
    {
        //check for subviews
        //avoid creating unnecessary array
        if ([view.subviews count])
        {
            [matches addObjectsFromArray:[view ad_viewsMatchingPredicate:predicate]];
        }
        else if ([predicate evaluateWithObject:view])
        {
            [matches addObject:view];
        }
    }
    return matches;
}

- (NSArray *)ad_viewsWithTag:(NSInteger)tag
{
    return [self ad_viewsMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused id bindings) {
        return [evaluatedObject tag] == tag;
    }]];
}

- (NSArray *)ad_viewsWithTag:(NSInteger)tag ofClass:(Class)viewClass
{
    return [self ad_viewsMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused id bindings) {
        return [evaluatedObject tag] == tag && [evaluatedObject isKindOfClass:viewClass];
    }]];
}

- (NSArray *)ad_viewsOfClass:(Class)viewClass
{
    return [self ad_viewsMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused id bindings) {
        return [evaluatedObject isKindOfClass:viewClass];
    }]];
}

- (UIView *)ad_firstSuperviewMatchingPredicate:(NSPredicate *)predicate
{
    if ([predicate evaluateWithObject:self])
    {
        return self;
    }
    return [self.superview ad_firstSuperviewMatchingPredicate:predicate];
}

- (UIView *)ad_firstSuperviewOfClass:(Class)viewClass
{
    return [self ad_firstSuperviewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *superview, __unused id bindings) {
        return [superview isKindOfClass:viewClass];
    }]];
}

- (UIView *)ad_firstSuperviewWithTag:(NSInteger)tag
{
    return [self ad_firstSuperviewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *superview, __unused id bindings) {
        return superview.tag == tag;
    }]];
}

- (UIView *)ad_firstSuperviewWithTag:(NSInteger)tag ofClass:(Class)viewClass
{
    return [self ad_firstSuperviewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *superview, __unused id bindings) {
        return superview.tag == tag && [superview isKindOfClass:viewClass];
    }]];
}

- (BOOL)ad_viewOrAnySuperviewMatchesPredicate:(NSPredicate *)predicate
{
    if ([predicate evaluateWithObject:self])
    {
        return YES;
    }
    return [self.superview ad_viewOrAnySuperviewMatchesPredicate:predicate];
}

- (BOOL)ad_viewOrAnySuperviewIsKindOfClass:(Class)viewClass
{
    return [self ad_viewOrAnySuperviewMatchesPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *superview, __unused id bindings) {
        return [superview isKindOfClass:viewClass];
    }]];
}

- (BOOL)ad_isSuperviewOfView:(UIView *)view
{
    return [self ad_firstSuperviewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *superview, __unused id bindings) {
        return superview == view;
    }]] != nil;
}

- (BOOL)ad_isSubviewOfView:(UIView *)view
{
    return [view ad_isSuperviewOfView:self];
}

//responder chain

- (UIViewController *)ad_firstViewController
{
    id responder = self;
    while ((responder = [responder nextResponder]))
    {
        if ([responder isKindOfClass:[UIViewController class]])
        {
            return responder;
        }
    }
    return nil;
}

- (UIView *)ad_firstResponder
{
    return [self ad_viewMatchingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, __unused id bindings) {
        return [evaluatedObject isFirstResponder];
    }]];
}

//frame accessors

- (CGPoint)ad_origin
{
    return self.frame.origin;
}

- (void)setAd_origin:(CGPoint)ad_origin
{
    CGRect frame = self.frame;
    frame.origin = ad_origin;
    self.frame = frame;
}

- (CGSize)ad_size
{
    return self.frame.size;
}

- (void)setAd_size:(CGSize)ad_size
{
    CGRect frame = self.frame;
    frame.size = ad_size;
    self.frame = frame;
}

- (CGFloat)ad_top
{
    return self.ad_origin.y;
}

- (void)setAd_top:(CGFloat)ad_top
{
    CGRect frame = self.frame;
    frame.origin.y = ad_top;
    self.frame = frame;
}

- (CGFloat)ad_left
{
    return self.ad_origin.x;
}

- (void)setAd_left:(CGFloat)ad_left
{
    CGRect frame = self.frame;
    frame.origin.x = ad_left;
    self.frame = frame;
}

- (CGFloat)ad_right
{
    return self.ad_left + self.ad_width;
}

- (void)setAd_right:(CGFloat)ad_right
{
    CGRect frame = self.frame;
    frame.origin.x = ad_right - frame.size.width;
    self.frame = frame;
}

- (CGFloat)ad_bottom
{
    return self.ad_top + self.ad_height;
}

- (void)setAd_bottom:(CGFloat)ad_bottom
{
    CGRect frame = self.frame;
    frame.origin.y = ad_bottom - frame.size.height;
    self.frame = frame;
}

- (CGFloat)ad_width
{
    return self.ad_size.width;
}

- (void)setAd_width:(CGFloat)ad_width
{
    CGRect frame = self.frame;
    frame.size.width = ad_width;
    self.frame = frame;
}

- (CGFloat)ad_height
{
    return self.ad_size.height;
}

- (void)setAd_height:(CGFloat)ad_height
{
    CGRect frame = self.frame;
    frame.size.height = ad_height;
    self.frame = frame;
}

- (CGFloat)ad_centerX {
    return self.center.x;
}

- (void)setAd_centerX:(CGFloat)ad_centerX {
    self.center = CGPointMake(ad_centerX, self.center.y);
}

- (CGFloat)ad_centerY {
    return self.center.y;
}

- (void)setAd_centerY:(CGFloat)ad_centerY {
    self.center = CGPointMake(self.center.x, ad_centerY);
}

- (CGFloat)ad_screenX {
    CGFloat x = 0.0f;
    for (UIView* view = self; view; view = view.superview) {
        x += view.ad_left;
        
        if ([view isKindOfClass:[UIScrollView class]]) {
            UIScrollView* scrollView = (UIScrollView*)view;
            x -= scrollView.contentOffset.x;
        }
    }
    
    return x;
}

- (CGFloat)ad_screenY {
    CGFloat y = 0;
    for (UIView* view = self; view; view = view.superview) {
        y += view.ad_top;
        
        if ([view isKindOfClass:[UIScrollView class]]) {
            UIScrollView* scrollView = (UIScrollView*)view;
            y -= scrollView.contentOffset.y;
        }
    }
    return y;
}

- (CGRect)ad_screenFrame {
    return CGRectMake(self.ad_screenX, self.ad_screenY, self.ad_width, self.ad_height);
}

//bounds accessors

- (CGSize)ad_boundsSize
{
    return self.bounds.size;
}

- (void)setAd_boundsSize:(CGSize)ad_size
{
    CGRect bounds = self.bounds;
    bounds.size = ad_size;
    self.bounds = bounds;
}

- (CGFloat)ad_boundsWidth
{
    return self.ad_boundsSize.width;
}

- (void)setAd_boundsWidth:(CGFloat)ad_width
{
    CGRect bounds = self.bounds;
    bounds.size.width = ad_width;
    self.bounds = bounds;
}

- (CGFloat)ad_boundsHeight
{
    return self.ad_boundsSize.height;
}

- (void)setAd_boundsHeight:(CGFloat)ad_height
{
    CGRect bounds = self.bounds;
    bounds.size.height = ad_height;
    self.bounds = bounds;
}

//content getters

- (CGRect)ad_contentBounds
{
    return CGRectMake(0.0f, 0.0f, self.ad_boundsWidth, self.ad_boundsHeight);
}

- (CGPoint)ad_contentCenter
{
    return CGPointMake(self.ad_boundsWidth/2.0f, self.ad_boundsHeight/2.0f);
}

- (void)ad_removeAllSubviews {
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}

- (void)ad_setTapActionWithBlock:(void (^)(void))block {
    self.userInteractionEnabled = YES;
    UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kADActionHandlerTapGestureKey);
    
    if (!gesture)
    {
        gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
        [self addGestureRecognizer:gesture];
        objc_setAssociatedObject(self, &kADActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
    }
    
    objc_setAssociatedObject(self, &kADActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}

- (void)ad_setTapActionWithGestureBlock:(void (^)(UITapGestureRecognizer *gesture))block {
    self.userInteractionEnabled = YES;
    UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kADActionHandlerTapGestureKey);
    
    if (!gesture)
    {
        gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
        [self addGestureRecognizer:gesture];
        objc_setAssociatedObject(self, &kADActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
    }
    
    objc_setAssociatedObject(self, &kADActionHandlerTapGestureBlockKey, block, OBJC_ASSOCIATION_COPY);
}

- (void)ad_setNoImpactTapActionWithBlock:(void (^)(void))block {
    self.userInteractionEnabled = YES;
    UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kADActionHandlerTapGestureKey);
    
    if (!gesture)
    {
        gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
        [self addGestureRecognizer:gesture];
        objc_setAssociatedObject(self, &kADActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
    }
    
    gesture.cancelsTouchesInView = NO;
    objc_setAssociatedObject(self, &kADActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}

- (void)ad_setTapActionWithFrame:(CGRect)frame block:(void (^)(void))block {
    self.userInteractionEnabled = YES;
    UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kADActionHandlerTapGestureKey);
    
    if (!gesture)
    {
        gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
        gesture.delegate = self;
        [self addGestureRecognizer:gesture];
        objc_setAssociatedObject(self, &kADActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
    }
    
    objc_setAssociatedObject(self, &kADActionHandlerTapFrameKey, [NSValue valueWithCGRect:frame], OBJC_ASSOCIATION_RETAIN);
    objc_setAssociatedObject(self, &kADActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}

- (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateRecognized)
    {
        NSValue *rect = objc_getAssociatedObject(self, &kADActionHandlerTapFrameKey);
        void(^action)(void) = objc_getAssociatedObject(self, &kADActionHandlerTapBlockKey);
        void(^gestureAction)(UITapGestureRecognizer *gesture) = objc_getAssociatedObject(self, &kADActionHandlerTapGestureBlockKey);
        
        BOOL isInRect = YES;
        if (rect) {
            CGPoint point = [gesture locationInView:self];
            isInRect = CGRectContainsPoint(rect.CGRectValue, point);
        }
        if (isInRect)
        {
            if (action) {
                action();
            }
            if (gestureAction) {
                gestureAction(gesture);
            }
        }
    }
}

- (void)ad_setLongPressActionWithBlock:(void (^)(void))block {
    self.userInteractionEnabled = YES;
    UILongPressGestureRecognizer *gesture = objc_getAssociatedObject(self, &kADActionHandlerLongPressGestureKey);
    
    if (!gesture)
    {
        gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForLongPressGesture:)];
        [self addGestureRecognizer:gesture];
        objc_setAssociatedObject(self, &kADActionHandlerLongPressGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
    }
    
    objc_setAssociatedObject(self, &kADActionHandlerLongPressBlockKey, block, OBJC_ASSOCIATION_COPY);
}

- (void)__handleActionForLongPressGesture:(UITapGestureRecognizer *)gesture {
    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        void(^action)(void) = objc_getAssociatedObject(self, &kADActionHandlerLongPressBlockKey);
        
        if (action)
        {
            action();
        }
    }
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    NSValue *rect = objc_getAssociatedObject(self, &kADActionHandlerTapFrameKey);
    if (rect) {
        CGPoint point = [gestureRecognizer locationInView:self];
        return CGRectContainsPoint(rect.CGRectValue, point);
    }
    return YES;
}

- (UIView *)ad_addStanderdShadow {
    self.layer.shadowPath    = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
    self.layer.shadowColor   = [UIColor blackColor].CGColor;
    self.layer.shadowOpacity = 0.0f;
    self.layer.shadowOffset  = CGSizeMake(2.0f, 1.0f);
    self.layer.shadowRadius  = 0.0f;
    self.layer.masksToBounds = NO;
    self.layer.cornerRadius  = 2.0;
    
    return self;
}

- (UIView *)ad_unreadDot:(BOOL)add offset:(CGPoint)offect {
    UIView *dot = objc_getAssociatedObject(self, &kADRedDotKey);
    [dot removeFromSuperview];
    if (add) {
        UIImageView *dot = [[UIImageView alloc] initWithFrame:CGRectMake(self.ad_size.width - 10 - offect.x, offect.y, 10, 10)];
        dot.backgroundColor = [UIColor colorWithRed:226/255.0 green:79/255.0 blue:50/255.0 alpha:1];
        dot.layer.cornerRadius = dot.ad_height/2.0;
        dot.layer.masksToBounds = YES;
        [self addSubview:dot];
        objc_setAssociatedObject(self, &kADRedDotKey, dot, OBJC_ASSOCIATION_RETAIN);
    }
    return self;
}

- (UIView *)ad_addCornerRadius:(CGFloat)cornerRadius corners:(UIRectCorner)corners {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
    return self;
}

@end

@implementation UIView (ADIBnspectable)

#pragma mark - setCornerRadius/borderWidth/borderColor
- (void)setCornerRadius:(CGFloat)cornerRadius {
    self.layer.cornerRadius = cornerRadius;
    self.layer.masksToBounds = cornerRadius > 0;
}

- (NSInteger)cornerRadius {
    return self.layer.cornerRadius;
}

- (void)setBorderWidth:(CGFloat)borderWidth {
    self.layer.borderWidth = borderWidth;
}

- (NSInteger)borderWidth {
    return self.layer.borderWidth;
}

- (void)setBorderColor:(UIColor *)borderColor {
    self.layer.borderColor = borderColor.CGColor;
}

- (UIColor *)borderColor {
    return [UIColor colorWithCGColor:self.layer.borderColor];
}

- (void)setBorderHexRgb:(NSString *)borderHexRgb {
    NSScanner *scanner = [NSScanner scannerWithString:borderHexRgb];
    unsigned hexNum;
    if (![scanner scanHexInt:&hexNum]) return;
    self.layer.borderColor = [self colorWithRGBHex:hexNum].CGColor;
}

-(NSString *)borderHexRgb {
    return @"0xffffff";
}

- (void)setMasksToBounds:(BOOL)bounds {
    self.layer.masksToBounds = bounds;
}

- (BOOL)masksToBounds {
    return self.layer.masksToBounds;
}

#pragma mark - hexRgbColor
- (void)setHexRgbColor:(NSString *)hexRgbColor {
    NSScanner *scanner = [NSScanner scannerWithString:hexRgbColor];
    unsigned hexNum;
    if (![scanner scanHexInt:&hexNum]) return;
    self.backgroundColor = [self colorWithRGBHex:hexNum];
}

- (UIColor *)colorWithRGBHex:(UInt32)hex {
    int r = (hex >> 16) & 0xFF;
    int g = (hex >> 8) & 0xFF;
    int b = (hex) & 0xFF;
    
    return [UIColor colorWithRed:r / 255.0f
                           green:g / 255.0f
                            blue:b / 255.0f
                           alpha:1.0f];
}


- (NSString *)hexRgbColor {
    return @"0xffffff";
}

#pragma mark - setOnePx
- (void)setOnePx:(BOOL)onePx {
    if (onePx) {
        CGRect rect = self.frame;
        rect.size.height = 1.0 / [UIScreen mainScreen].scale;
        self.frame = rect;
    }
}

- (BOOL)onePx {
    return self.onePx;
}

@end
